﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Antlr.Runtime;
using Antlr.Runtime.Tree;
using Antlr.Runtime.Debug;
using Antlr.Utility.Tree;
using Antlr.StringTemplate;

using SscLib;

namespace Ssc
{


    public class Program
    {

        public static IErrorManager errMgr;
        public static bool printAST = false;
        public static bool dbg = false;
        public static bool dbgST = false;
        public static bool printSym = false;
        public static bool dot = false;
        public static bool genCode = false;

        
        public static int Main(string[] args)
        {
            errMgr = new ErrorManager();

            List<String> files = new List<string>();
            foreach (string arg in args)
            {
                switch (arg)
                {
                        //generate code
                    case "-gen":
                        genCode = true;
                        break;
                    case "-dot":
                        dot = true;
                        break;
                    //debug: print parse tree (requires antlr to generate parser with -debug option)
                    case "-dbg":
                        dbg = true;
                        break;
                    //print abstract syntax tree
                    case "-ast":
                        printAST = true;
                        break;
                    case "-dbgST":
                        dbgST = true;
                        break;
                    case "-sym":
                        printSym = true;
                        break;
                    default:
                        if (arg.StartsWith("-")) Console.WriteLine("invalid option: " + arg);
                        else files.Add(arg);
                        break;
                }
            }
            foreach (String file in files)
            {
                try
                {
                    Process(file);
                }
                catch (FileNotFoundException fnfe)
                {
                    Console.WriteLine("sim# file " + file + " not found");
                }


            }

            return errMgr.HasError ? -1:0;
        }

        public static void Process(String filename)
        {
            ICharStream f = null;
            f = new ANTLRFileStream(filename);

            try
            {
                SimSharpLexer lex = new SimSharpLexer(f);
                lex.errMan = errMgr;
                CommonTokenStream tokens = new CommonTokenStream(lex);

                SimSharpParser g = null;


#if(ANTLRDBG)
            ParseTreeBuilder b = null;
            if (dbg)
            {
                 b = new ParseTreeBuilder("simulaSourceModule");
                 g = new SimSharpParser(tokens, b);
            }
            else
            {
                b = new ParseTreeBuilder("");
                g = new SimSharpParser(tokens, b);
            }
#else
                g = new SimSharpParser(tokens);
#endif
                g.TreeAdaptor = new SimSharpTreeAdaptor();
                g.errMan = errMgr;

                //g.TreeAdaptor = myadaptor;
                SymbolTable symTab = new SymbolTable();
                SimSharpParser.begin_return r = g.begin(symTab);//.simulaSourceModule();

#if(ANTLRDBG)
            if (dbg)
            {
                Console.WriteLine("Parse Tree:\n" + b.Tree.ToStringTree());
                Console.WriteLine("Input String:\n" + b.Tree.ToInputString());
            }
#endif

                CommonTree t = (CommonTree)r.Tree;

                if (printAST)
                {
                    Console.WriteLine("Syntax tree after parsing:");
                    DumpAST(t.ToStringTree());

                }

                if (t != null && !errMgr.HasError)
                {

                    //if (printAST)
                    //{
                    //    Console.WriteLine("Syntax tree after parsing:");
                    //    DumpAST(t.ToStringTree());

                    //}
                    if (dot)
                    {
                        try
                        {
                            DOTTreeGenerator dotGen = new DOTTreeGenerator();

                            StringTemplate st = dotGen.ToDOT(t);
                            string dotFile = Path.ChangeExtension(filename, "dot");
                            File.Delete(dotFile);
                            File.AppendAllText(dotFile, st.ToString());
                        }
                        catch (IOException ioe)
                        {
                            Console.WriteLine(ioe.Message);
                        }

                    }
                }

                CommonTreeNodeStream nodes = new CommonTreeNodeStream(t);
                SimSharpAnalyse analyse = new SimSharpAnalyse(nodes);
                
                analyse.errMan = errMgr;

                analyse.begin(symTab);


                if (t != null && !errMgr.HasError)
                {

                    if (printAST)
                    {
                        Console.WriteLine("Syntax tree after semantic analysis:");
                        DumpAST(t.ToStringTree());
                    }

                    if (printSym)
                    {
                        Console.WriteLine("Symbol table after semantic analysis:");
                        Console.WriteLine(symTab + "\n");
                    }

                    if (genCode)
                    {
                        nodes.Reset();
                        SimSharpGen gen = new SimSharpGen(nodes);
                        gen.errMan = errMgr;
                        CodeGen cg = new CodeGen(filename, symTab);

                        CreateTypeMetadata(cg, symTab.TopScope);
                        foreach (TypeSymbol at in symTab.ArrayTypes)
                            cg.CreateArrayMetadata(at);
                        CreateFieldMetadata(cg, symTab.TopScope);
                        CreateCtorMetadata(cg, symTab.TopScope);
                        //CreateMethodMetadata(cg, symTab.TopScope);


                        gen.begin(symTab, cg);

                        cg.WritePEFile();
                    }
                }
            }
            catch (RecognitionException re)
            {
                Console.WriteLine(re);
            }
            catch (Exception e)
            {
                errMgr.ReportError("Ssc", null, "Internal Compiler Error", null, e);
                Console.WriteLine("panic:\n" + e);
            }
        }

        private static void CreateTypeMetadata(CodeGen cg, ScopeSymbol scope)
        {

            foreach (Symbol s in scope.Members.Values)
            {
                if (s is ProgramSymbol) cg.CreateMetadata((ProgramSymbol)s);
                else if (s is TypeSymbol) cg.CreateMetadata((TypeSymbol)s);

                if (s is ScopeSymbol) CreateTypeMetadata(cg, (ScopeSymbol)s);
            }
        }

        private static void CreateFieldMetadata(CodeGen cg, ScopeSymbol scope)
        {

            foreach (Symbol s in scope.Members.Values)
            {
                if (s is FieldSymbol) cg.CreateMetadata((FieldSymbol)s);

                if (s is ScopeSymbol) CreateFieldMetadata(cg, (ScopeSymbol)s);
            }
        }

        private static void CreateCtorMetadata(CodeGen cg, ScopeSymbol scope)
        {

            foreach (Symbol s in scope.Members.Values)
            {
                if (s is CtorSymbol) cg.CreateMetadata((CtorSymbol)s);
                if (s is MethodSymbol) cg.CreateMetadata((MethodSymbol)s);

                if (s is ScopeSymbol) CreateCtorMetadata(cg, (ScopeSymbol)s);
            }
        }

        private static void DumpAST(string s)
        {
            StringBuilder sb = new StringBuilder();

            int start = 0;
            int end = 0;
            int indent = -1;
            int temp = 0;

            for (int i = 0; i < s.Length; i++)
            {
                switch (s[i])
                {
                   // case ' ':
                    case '(':
                        while (s[start] == ' ') start++;
                        if (i > start)
                        {
                            end = i;
                            temp = indent;
                            while (temp-- > 0) sb.Append(' ');
                            sb.AppendLine(s.Substring(start, end - start));
                        }
                        indent++;
                        start = i;
                        break;
                    case ')':
                        end = i + 1;
                        temp = indent--;
                        while (temp-- > 0) sb.Append(' ');
                        sb.AppendLine(s.Substring(start, end - start ));
                        start = end;
                        break;
                }
            }
            Console.WriteLine(sb.ToString());
        }
    }

}

